//
// Created by king on 2018/11/14.
//

#include "common.h"
#include "gl_util.h"
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>


using namespace std;

static void checkError(std::string line) {
    GLint err = glGetError();
    if (err != GL_NO_ERROR) {
        LOGE("check error: %s", line.c_str());
    }
}

GLuint linkProgram(const char * pVertexSource, const char * pFragmentSource)
{
    if (!pVertexSource) {
        LOGE("vertex shader is empty!");
        return 0;
    }

    if (!pFragmentSource) {
        LOGE("fragment shader is empty!");
        return 0;
    }

    GLuint vertexShaderObjId = compileShader(GL_VERTEX_SHADER, pVertexSource);


    if (!vertexShaderObjId) {
        return 0;
    }
    GLuint fragmentShaderObjId = compileShader(GL_FRAGMENT_SHADER, pFragmentSource);
    if (!fragmentShaderObjId) {
        return 0;
    }

    GLuint program = glCreateProgram();
    std::string err = "create program";
    checkError(err);

    if (program) {
        glAttachShader(program, vertexShaderObjId);
        glAttachShader(program, fragmentShaderObjId);

        glLinkProgram(program);

        GLint linkStatus = checkProgramStatus(program);
        if (!linkStatus) {
            LOGE("glLinkProgram status %d", linkStatus);
            glDeleteProgram(program);
            return 0;
        }
    }

    return program;
}


static GLuint compileShader(GLenum type, const char* pSource) {
    GLuint shader = glCreateShader(type);

    std::string err = "createShader type:" + to_string(type);
    checkError(err);

    if (shader) {
        glShaderSource(shader, 1, &pSource, NULL);
        err = "shader source";
        checkError(err);

        glCompileShader(shader);
        err = "compileShader";
        checkError(err);

        GLint compiled = checkShaderStatus(shader);
        if (!compiled) {
            LOGE("compileShader compiled %d", compiled);
            glDeleteShader(shader);
            return 0;
        }

        return shader;
    }
    return 0;
}

static GLint checkShaderStatus(GLuint shader)
{
    GLint compiled = 0;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    if (!compiled) {
        GLint infoLen = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen) {
            char* buf = (char*) malloc(infoLen);
            if (buf) {
                glGetShaderInfoLog(shader, infoLen, NULL, buf);
                LOGE("Could not compile shader %d:\n%s\n",
                     shader, buf);
                free(buf);
            }
        }
    }

    return compiled;
}

static GLint checkProgramStatus(GLuint program)
{
    GLint linkStatus = GL_FALSE;
    glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
    if (linkStatus != GL_TRUE) {
        GLint bufLength = 0;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
        if (bufLength) {
            char* buf = (char*) malloc(bufLength);
            if (buf) {
                glGetProgramInfoLog(program, bufLength, NULL, buf);
                LOGE("Could not link program:\n%s\n", buf);
                free(buf);
            }
        }
    }
    return linkStatus;
}